「絕對不要相信來自於使用者的資料。」
寫 PHP 時常常會聽到一句「絕對不要相信來自於使用者的資料」,這麼多年過去了,SQL Injection、XSS、LFI 等問題在 PHP 上仍然層出不窮。
有人把原因歸咎於 PHP 的語言設計:過於複雜化的內建函式庫。就算是多年 PHP 經驗的老手,也可能會落入一些不是最佳實踐的陷阱。
XSS,全名 Cross-Site Scripting。簡單來說就是惡意使用者讓網頁渲染出非預期的內容,進而達成某些目的。
<?php
echo 'Hello, '.$_GET['name'];
此時,若我使用 /?name=<script>alert('XSS')</script>
就可以直接跳出一個 alert。
然而,跳出 alert 並非 XSS 主要的危害,通常僅是拿來驗證問題存在的手段。XSS 真正的危險在於:它可以取得這個網站的 cookie 等資訊--這通常是拿來存在使用者憑證的地方。
當然,XSS 的危害不止於盜竊用戶憑證,例如透過今年的 CVE-2019-5786 在 Chrome 上執行後直接在對方電腦執行程式。
這邊先舉幾個比較常見的防範 XSS 的錯誤範例。
echo str_replace('script', '', $_GET['name']);
破解法:/?name=<sscriptcript>alert('XSS')</sscriptcript>
strip_tags()
echo strip_tags($_GET['name']);
用 strip_tags
的幾個問題
allowed_tags
(strip_tags
的第二個參數)可能仍然會存在 XSS 問題echo strip_tags('<img src="a.jpg" onload="alert(1)"/>', '<img>');
上述內容中,<img>
是可使用的 tag,所以內容依然會被輸出,而 onload
事件中可以執行 Javascript,故仍然可以觸發 XSS。
就理論上來說,htmlspecialchars()
與 htmlentities()
都是正確答案。
在 Modern PHP 中使用的是 htmlentities()
,而在各大 Framework 的實作上以 htmlspecialchars()
居多。
htmlspecialchars()
是僅替換需要進行 HTML 編碼的字元(<
、>
、&
、"
及 '
)
幾個比較知名的 Framework 或 Template Engine 都是使用這個函式:
值得注意的是,在使用這個函式時務必要加上合適的參數。
htmlentities()
會將所有輸入的內容都進行 HTML 編碼。
這個函式的缺點是如果使用非預期的編碼,可能會造成輸出時仍然存在亂碼或利用編碼問題繞過並達成 XSS。對於 PHP 5.5 (含)以後的預設編碼是 UTF-8;5.4 (含)以前的預設編碼是 ISO-8859-1。
在 Modern PHP 一書中,使用這個函式做輸出內容的編碼,但作者提到一定要注意參數的設置:
ENT_QUOTES
)今天的 XSS 算是把之前在 PTT PHP 版上的文章中關於 XSS 的部份稍微拿出來再整理一次。
當時跟 GLINE 大大討論 htmlentities 在某些時候的預設設定是可以用 UTF-7 打穿的,這部份僅僅是帶過而已,這邊再做比較詳細的整理。
該篇文章有我跟 G 大整理出的一系列 PHP 網站資安常見的問答(之後鐵人要是沒東西寫的話也可能拿來整理 XD),可以稍微參考看看:
[請益] 網站資安需注意哪些?
R: [請益] 網站資安需注意哪些?
R: [請益] 網站資安需注意哪些?
R: [請益] 網站資安需注意哪些?